/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.kairos.transitions
{
	import flash.events.Event;
	
	import com.bourre.commands.ASyncCommand;
	import com.bourre.error.NullPointerException;
	import com.bourre.transitions.AbstractTween;
	import com.bourre.transitions.Tween;
	import com.kairos.engine.RTAnimation;
	import com.kairos.engine.RTBeacon;
	import com.kairos.engine.RTEvent;
	import com.kairos.log.KairosDebug;	

	/**
	 * <code>TweenRT</code> is a framerate independant motion tween that works
	 * according to all <code>RTBeacon</code> features. <code>TweenRT</code>
	 * defines duration in milliseconds as all others <code>RTObject</code>.
	 * <p>
	 * <code>TweenRT</code> extends <code>AbstractTween</code> from LowRA
	 * and add support for time global and local scaling according to the
	 * <code>RTAnimation</code> interface.
	 * </p><p>
	 * Tweens support properties and function as target field for the animation.
	 * When specifying a function as target you also have to define the <code>getter</code>
	 * parameter to offer an access at the object to get start value from the target object.
	 * </p><p>
	 * By default, a tween don't need a start value, it take the current target
	 * property value as start value, if the target property is a function you have
	 * to define the getter parameter when creating the tween.
	 * </p><p>
	 * You can use easing functions in order to modify the values interpolations
	 * as with any others <code>Tween</code> objects.
	 * </p>
	 * 
	 * @author 	Cédric Néhémie
	 * @see		AbstractTween
	 * @see		BasicTween
	 * @see		RTAnimation
	 * @see		ASyncCommand
	 * @example Creating a tween on a single property of a sprite.
	 * 
	 * <listing>import com.kairos.transtions.TweenRT;
	 * import flash.display.Sprite;
	 * 
	 * var sprite : Sprite = new Sprite();
	 * var tween : TweenRT = new TweenRT( sprite, "x", 500, 1000 );
	 * tween.start();</listing>
	 * 
	 * Creating a tween on a single method of a custom object.
	 * In that example the start and easing are set respectively
	 * on <code>NaN</code> and <code>null</code> to keep the
	 * default behavior.
	 * 
	 * <listing>import com.kairos.transtions.TweenRT;
	 * 
	 * var target : MyCustomClass = new MyCustomClass();
	 * var tween : TweenRT = new TweenRT( target, "setProperty", 500, 1000, NaN, null, "getProperty" );
	 * tween.start();</listing>
	 */
	public class TweenRT extends AbstractTween implements Tween, RTAnimation, ASyncCommand
	{
			
		/**
		 * Local speed factor for the object
		 */	
		protected var _nLocalSpeed : Number;	
		
		/**
		 * Create a new tween for the passed-in target object.
		 * <p>
		 * By default, a tween don't need a start value, it take the current target
		 * property value as start value, if the target property is a function you have
		 * to define the <code>getter</code> parameter when calling the constructor in
		 * order to provide to the tween an access to the start value.
		 * </p><p>
		 * If the easing function is ommitted the <code>noEasing</code> method
		 * of the <code>AbstractTween</code> class.
		 * </p>
		 * 
		 * @param	target		<code>Object</code> target of the tween
		 * @param	setter		<code>String</code> property name of the target object
		 * @param	end			<code>Number</code> end value of the tween.
		 * @param	duration	<code>Number</code> duration of the tween in milliseconds
		 * @param	start		<code>Number</code> start value of the tween
		 * @param	easing		<code>Function</code> for interpolation
		 * @param	getter		<code>String</code> getter name if property is a setter function.
		 * @example	Creating a tween to manipulate a <code>DisplayObject</code>
		 * <listing>import com.kairos.transtions.TweenRT;
		 * import flash.display.Sprite;
		 * 
		 * var sprite : Sprite = new Sprite();
		 * var tween : TweenRT = new TweenRT ( sprite, "x", 500, 1000 );
		 * 
		 * tween.start();</listing>
		 */
		public function TweenRT( target : Object, setter : String, end:Number, duration:Number, start:Number = NaN, easing:Function = null, getter : String = null )
		{
			_oBeacon = RTBeacon.getInstance();
			_nLocalSpeed = 1;
			super( target, setter, end, duration, start, easing, getter );
		}
		
		
		/**
		 * Proceed to the animation of the tween.
		 * <p>
		 * That method have been override to support the real time processing
		 * implementation of Kairos.
		 * </p>
		 * @param	e 	<code>Event</code> of <code>TickListener</code> implementation.
		 */
		override public function onTick( e : Event = null ) : void
		{
			_nPlayHead += ( e as RTEvent ).getStep() * _nLocalSpeed * ( _bReversedMotion ? -1 : 1 );
			super.onTick( e );
		}
		
		/**
		 * Returns <code>true</code> if the animation finish after
		 * the current move.
		 * 
		 * @return 	<code>true</code> if the animation finish after
		 * 			the current move.
		 */
		override public function isMotionFinished() : Boolean
		{
			return _nPlayHead >= _nDuration;
		}
		
		/**
		 * Returns <code>true</code> if the animation finish after
		 * the current move.
		 * 
		 * @return 	<code>true</code> if the animation finish after
		 * 			the current move.
		 */
		override public function isReversedMotionFinished() : Boolean
		{
			return _nPlayHead <= 0;
		}
		
		
		/**
		 * Defines a new <code>TickBeacon</code> object the object will run on.
		 * <p>
		 * Use that method carefully, if you have objects in your scene which 
		 * are plugged on two beacon which have differents settings you can loose
		 * synchronisation between those objects.
		 * </p><p>
		 * If the passed-in beacon is null, the function failed and throw an error
		 * on the Kairos log channel.
		 * </p>
		 * @param	b	<code>TickBeacon</code> to register with.
		 * @throws 	<code>NullPointerException</code> — The passed-in beacon is null.
		 */
		public function setTickBeacon( b : RTBeacon ) : void
		{
			if( !b ) 
			{
				KairosDebug.WARN( "Trying to set a null beacon as beacon for " + this );
				throw new NullPointerException ( "Trying to set a null beacon as beacon for " + this );
			}
			
			if( _bIsRunning )
			{ 
				stop();
				_oBeacon = b;
				start();
			}
			else
			{
				_oBeacon = b;
			}
		}
		
		/**
		 * Returns the <code>RTBeacon</code> used by the current object.
		 * 
		 * @return <code>RTBeacon</code> used by the current object.
		 */
		public function getTickBeacon () : RTBeacon
		{
			return _oBeacon as RTBeacon;
		}
		
		/**
		 * Defines the local speed factor for the current object. Local speed works
		 * as a multiplier for the time step value received from the beacon - as the
		 * <code>setGlobalSpeed</code> feature of the RTBeacon.
		 * <p>
		 * The default <code>setLocalSpeed</code> method don't support value lower
		 * or equals than 0, override it if you want to avoid negative values.
		 * </p>
		 * @param	rate float number used as multiplier for the current time step
		 * @throws 	<code>RangeError</code> — The value specified in <code>setLocalSpeed</code>
		 * 			isn't supported by the current object.	
		 */
		public function setLocalSpeed ( rate : Number ) : void
		{
			if( rate <= 0 )
			{
				KairosDebug.ERROR( rate + " can't be lower or equals than 0 in " + this + ".setLocalSpeed()" );
				throw new RangeError (" The value specified in setLocalSpeed isn't supported by the current object." );	
			}
			_nLocalSpeed = rate;
		}
		
		/**
		 * Returns the current local speed factor of the object.
		 * 
		 * @return <code>Number</code> local speed of the object.
		 */
		public function getLocalSpeed () : Number
		{
			return _nLocalSpeed;
		}

	}
}